# -*- coding: utf-8 -*-
from PyQt4 import QtCore, QtGui, Qt
from PyQt4.QtGui import QMessageBox
from container import Ui_MainWindow
import time,uuid,urllib,tempfile,os,shutil
from PyQt4 import QtNetwork  #for cx_freeze build
import serial
from PyQt4.QtWebKit import QWebSettings
import json,traceback
from printer import pprint
from ConfigParser import ConfigParser
from printer import test_pprint
import re
import socket
import sys
from pay.yeepay import KeyboardClient
from pay.yeepay import YeepayClient
import binascii
from pay.myPyDes import MyDes
import zipfile
from pay.yeepay import Utils

VERSION = 0.1
#CURRENTDIR = os.path.dirname(__file__)
CURRENTDIR = os.path.abspath('.')
FILE_NAME = 'countCoin'

select_coin = '%c' * 8 % (0x02,0x08,0x10,0x7f,0x10,0x00,0x03,0x77)  #查询数据

clear_coin = '%c' * 8 % (0x02,0x08,0x10,0x7f,0x10,0x00,0x03,0x77)  #关闭投币器（清除数据）
#local_printer = "rfprint_80"

class main_app(object):
    def __init__(self):
        self.mainwindow = QtGui.QMainWindow()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self.mainwindow)
        self.ui.webView.setUrl(QtCore.QUrl(conf['url']))
        websettings = self.ui.webView.settings()
        websettings.setAttribute(QWebSettings.AutoLoadImages,True)
        websettings.setAttribute(QWebSettings.PluginsEnabled,True)
        websettings.setAttribute(QWebSettings.JavascriptEnabled,True)
        
        self._connect(self.ui.webView.page().mainFrame(), "javaScriptWindowObjectCleared()", 
                      self.insert_js_object)
        
        self.updater = updater()
        #客户端新版本更新
        self.updater.start()

    def insert_js_object(self):
        self.jsobj = jsbridge(self)
        self.ui.webView.page().mainFrame().addToJavaScriptWindowObject("pyObj", self.jsobj)

    def _connect(self, sender, signal, slot):
        QtCore.QObject.connect(sender, QtCore.SIGNAL(signal), slot)

    def show(self):
        if conf['stayontop'] == 'true':
            self.mainwindow.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
        if conf['fullscreen'] == 'true':
            self.mainwindow.showFullScreen()
        else:
            self.mainwindow.show()
        #self.mainwindow.showMaximized() 
        
    def invoke_js(self,func_str):
        self.ui.webView.page().mainFrame().evaluateJavaScript(func_str)


class jsbridge(QtCore.QObject):
    "暴露为页面的js对象,包含与页面交互的方法"
    def __init__(self,app):
        super(jsbridge,self).__init__()
        self.app = app
        #self.printer = self.get_printer()
        # if not self.printer:
        #     QMessageBox.critical(None,u"消息",u"无法加载彩票打印机,程序将退出",QMessageBox.Yes)
        #     sys.exit(1)

        #这里coin_timer一定要类变量，局部变量会导致方法结束后，coin_timer被销毁
        self.coin_timer = coin_acceptor2()
        self.coin_timer.init(0,conf['coin_port'])
        if not self.coin_timer.started:
            self.coin_timer.start()
            self.coin_timer.started=True
        self.app._connect(self.coin_timer, "update_coin(int)", 
                      self.update_coin)
        self.app._connect(self.coin_timer, "coin_complete()", 
                      self.coin_complete)
        
#        self.net_checker = NetworkChecker()
#        self.app._connect(self.net_checker, "network_status(int)", 
#                      self.network_status)
#        self.net_checker.start()
        
    # def get_printer(self):
    #     printerInfo = QtGui.QPrinterInfo()
    #     for item in printerInfo.availablePrinters():
    #         if local_printer r== item.printerName():
    #             printer = QtGui.QPrinter(item)
    #             printer.setOutputFormat(QtGui.QPrinter.NativeFormat)
    #             return printer

    @QtCore.pyqtSlot(result=str)
    def get_if_mac(self):
        return get_mac()

    def update_coin(self,coin_ready):
        method = QtCore.QString("update_coin(%1);").arg(coin_ready);
        self.app.invoke_js(method)
        
    def version_updating(self):
        self.app.invoke_js("version_updating();")

    @QtCore.pyqtSlot(str,str,result=bool)
    def print_lottery(self,lottery_code,data):
        try:
            result = pprint(lottery_code,json.loads(str(data)),conf['printer_port'])
            return result
        except:
            traceback.print_exc()
            return False

    def coin_complete(self):
        self.app.invoke_js("coin_complete();")
        
    def network_status(self,status):
        method = QtCore.QString("network_status(%1);").arg(status);
        self.app.invoke_js(method)
        
    @QtCore.pyqtSlot(int,result=bool)
    def start_take_coin(self,total):
        self.coin_timer.init(total,conf['coin_port'])
        return True
    
    @QtCore.pyqtSlot(result=str)
    def get_coin(self):
        diff_time = time.time() - self.coin_timer.start_coin_time
        if diff_time>int(conf['coin_time_out']):
            self.coin_timer.coins = 0
        return '%s'%self.coin_timer.coins
    
    @QtCore.pyqtSlot(result=str)
    def clear_total(self):
        self.coin_timer.total = 0
        return 'ok'
    
    @QtCore.pyqtSlot(str,result=bool)
    def input_bank_password(self,card_number):
        keyboard_client = KeyboardClient(card_number)
        
        self.app._connect(keyboard_client,"update_password(int)",self.update_password)
        self.app._connect(keyboard_client,"complete_password()",self.complete_password)
        
        result = keyboard_client.read_pin_block()
        
        return result
        
    def update_password(self,status):
        #status取值,1:新输入一位密码；2：更正密码，重新输入密码
        method = QtCore.QString("update_password(%1);").arg(status);
        self.app.invoke_js(method)
        
    def complete_password(self):
        self.app.invoke_js("complete_password();")
        
    
    @QtCore.pyqtSlot(result=bool)
    def test_print(self):
        try:
            test_pprint(conf['printer_port'])
            return True
        except:
            traceback.print_exc()
            return False
        
    @QtCore.pyqtSlot(result=bool)
    def clearMoneyBox(self):
        try:
            coin_path = os.path.join(CURRENTDIR,FILE_NAME)
            with file(coin_path, 'w') as f:
                f.write('0')
            f.close()
            return True
        except:
            traceback.print_exc()
            return False
        
    @QtCore.pyqtSlot(result=bool)
    def poweroff(self):
        try:
            cmd = "c:\\Windows\\System32\\Shutdown.exe -s -t 0"
            os.system(cmd)
            return True
        except:
            traceback.print_exc()
            return False
        
    @QtCore.pyqtSlot(result=int)
    def countMoneyBox(self):
        try:
            coin_path = os.path.join(CURRENTDIR,FILE_NAME)
            if not os.path.exists(coin_path):return 0
            with file(coin_path) as f:
                result = f.readline()
            f.close()
            return int(result) if result else 0
        except:
            traceback.print_exc()
            return 0
        
    @QtCore.pyqtSlot(result=bool)
    def yeepay_signin(self):
        '''易宝支付签到'''
        yeepay = YeepayClient()
        result = yeepay.signin()
        return result
        
    @QtCore.pyqtSlot(str,str,result=str)
    def yeepay_consume(self,money,card_number):
        '''银联卡消费，发起金融消费包'''
        yeepay = YeepayClient()
        try:
            result=yeepay.consume(money,card_number)
        except Exception as e:
            print 'exception is:',e
            result = 'exception'
        return result
        
    @QtCore.pyqtSlot(result=bool)
    def yeepay_signout(self):
        '''易宝支付签退'''
        yeepay = YeepayClient()
        result=yeepay.signout()
        return result
        
    @QtCore.pyqtSlot(str,str,result=bool)
    def yeepay_balance(self,balance_nums,balance_amount):
        '''易宝支付结算'''
        yeepay = YeepayClient()
        result=yeepay.balance(balance_amount,balance_nums)
        return result
        
    @QtCore.pyqtSlot(result=str)
    def yeepay_balance_info(self):
        '''易宝支付结算信息统计'''
        number = len(YeepayClient._CONSUME_INFO)
        total_money = 0
        for info in YeepayClient._CONSUME_INFO:
            total_money += int(info['money'])
        result = str(number)+"#"+str(total_money/100)+'.'+str(total_money%100)
        return result
    
    @QtCore.pyqtSlot(result=str)
    def yeepay_rollback(self):
        yeepay = YeepayClient()
        rollback_data = YeepayClient._ROLLBACK_DATA
        yeepay.rollback_consume(rollback_data['money'], rollback_data['serial_number'], rollback_data['errorcode'], rollback_data['value60'], rollback_data['mac'])
        return 'ok'
        
class Timer(QtCore.QThread):
    def __init__(self,parent=None):
        super(Timer,self).__init__(parent)
        self.stoped = False
        self.mutex = QtCore.QMutex()

    def run(self):
        with QtCore.QMutexLocker(self.mutex):
            self.stoped = False
        while True:
            if self.stoped:
                return
            try:
                self._run()
            except LoopingCallDone:
                break

    def stop(self):
        with QtCore.QMutexLocker(self.mutex):
            self.stoped = True
        
    def isStoped(self):   
        with QtCore.QMutexLocker(self.mutex):
            return self.stoped

class updater(Timer):
    def __new__(cls, *args, **kw):  
        if not hasattr(cls, '_instance'):  
            orig = super(updater, cls)  
            cls._instance = orig.__new__(cls, *args, **kw)  
        return cls._instance
    
    def _run(self):
        #检查版本,http://172.16.10.64:8081/version
        #对比版本，如果有新版本则下载library.zip并替换本地文件
        if YeepayClient._MAC_KEY:
            yeepay=YeepayClient()
            yeepay.signin()
        try:
            _version = conf['version']
            base_url = conf['url'][:-1] if conf['url'].endswith('/') else conf['url']
            result = urllib.urlopen('%s/%s'%(base_url,'version'))
            version = result.read().strip()
            print "====== need update? ",float(version) > float(_version),"========"
            if (float(version) > float(_version)):
#                self.do_update()
                self.download('library.zip')
                self.download('lottery.conf')
                print '======= do update complete =========='
        except Exception as e:
            print "an exception occured:%s"%e
           
        time.sleep(12*60*60) #12个小时检查一次
        #time.sleep(6)
        
    def download(self,filename):
        temp = tempfile.mktemp()
#        libzip = urllib.urlretrieve(os.path.join(conf['url'],'%s?name=%s' % (filename,get_mac())),temp)
        base_url = conf['url'][:-1] if conf['url'].endswith('/') else conf['url']
        urllib.urlretrieve('%s/static/%s?name=%s' % (base_url,filename,time.time()),temp)
        shutil.move(temp,os.path.join(CURRENTDIR,filename))
        
    def do_update(self):
        temp = tempfile.mktemp()
        base_url = conf['url'][:-1] if conf['url'].endswith('/') else conf['url']
        urllib.urlretrieve('%s/static/lotteryClient.zip?name=%s' % (base_url,time.time()),temp)
        
        zf=zipfile.ZipFile(temp)
        for f in zf.namelist():
            if f.rfind('yeepaydata.conf') != -1: #流水批次号配置文件不覆盖
                pass
            else:
                zf.extract(f, CURRENTDIR)
                try:
                    shutil.copy2(os.path.join(CURRENTDIR,f), CURRENTDIR)
                except Exception as e:
                    print 'an exception occured in update version:%s'%e
                
        extract_file_path=os.path.join(CURRENTDIR,'lottery_terminal')
        if os.path.exists(extract_file_path):
            shutil.rmtree(extract_file_path)
        os.remove(r'%s'%temp) #删除临时文件
        
class NetworkChecker(Timer):
    '''网络检查器，检测网络是否畅通'''
    def __init__(self,parent=None):
        Timer.__init__(self)
        p=re.compile('http://([\w\.]+):(\w+)')
        _,ip,port,_ = p.split(conf['url'])
        self.ip = ip
        self.port = int(port)
        self.status = True #初始为网络正常，状态值只要一改变就发送信号来改变前台的展示内容
        
    def _run(self):
        sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        try:
            sock.connect((self.ip,self.port))
            if not self.status: #之前网络为不正常状态
                self.status = True
                self.emit(QtCore.SIGNAL("network_status(int)"),1)
        except:
            if self.status: ##之前网络为正常状态
                self.status = False
                self.emit(QtCore.SIGNAL("network_status(int)"),0)
        time.sleep(300)

class coin_acceptor2(Timer):
    '''硬币接收器'''
    def __new__(cls, *args, **kw):  
        if not hasattr(cls, '_instance'):
            orig = super(coin_acceptor2, cls)
            cls._instance = orig.__new__(cls, *args, **kw)
            cls._instance.take_serial = False #是否占用了端口
            cls._instance.coins = 0
            cls._instance.start_coin_time = time.time()
            cls._instance.started = False
        return cls._instance
    
    def init(self,total,coin_port):
        self.total = total
        self.coin_port = coin_port
        if time.time() - self.start_coin_time>int(conf['coin_time_out']): 
            self.coins = 0
        if self.total>0 and self.coins>=self.total:
            self.emit(QtCore.SIGNAL("coin_complete()"))
            self.coins = self.coins-self.total
            self.total = 0
        
    def _run(self):
        if not self.take_serial:
            self.s = serial.Serial(self.coin_port,baudrate=9600)
            self.take_serial = True
            
        try:
            data=binascii.b2a_hex(self.s.read(2))
        except Exception as e:
            print 'exception is:%s'%e
        
        if data=='0001': #有硬币
            self.start_coin_time = time.time()
            self.coins = self.coins + 1
            self.emit(QtCore.SIGNAL("update_coin(int)"),self.coins)
            persistent_coin()
        if self.coins == self.total and self.total>0:
            self.emit(QtCore.SIGNAL("coin_complete()"))
            self.coins = 0
            self.total = 0
#            raise LoopingCallDone()

def persistent_coin():
    coin_path = os.path.join(CURRENTDIR,FILE_NAME)
    if not os.path.exists(coin_path):
        with file(coin_path,'w') as f:
            f.write('1')
        f.close()
    else:
        with file(coin_path) as f:
            oldnum = f.readline()
        f.close()
        oldnum = 0 if not oldnum else int(oldnum)
        newnum = oldnum + 1
        with file(coin_path,'w') as f:
            f.write(str(newnum))
        f.close()

class LoopingCallDone(Exception): pass

def get_mac():
    '''
    由于硬件生产问题, mac都一样, 现在想法是记录一个uuid到本地
    '''
    licence_path=os.path.join(CURRENTDIR, 'licence')
    licence_code=uuid.uuid1().get_hex()
    if os.path.exists(licence_path):
        with file(licence_path) as f:
            licence_code=f.readline()
    else:
        licence_code = ''
#        with file(licence_path, 'w') as f:
#            f.write(licence_code)
#        f.close()
    return licence_code

def load_config():
    c = ConfigParser()
    try:
        c.read(os.path.join(CURRENTDIR,'lottery.conf'))
        return dict(c.items('default'))
    except Exception as e:
        print "can not read config : lottery.conf %s"%e
        sys.exit(-1)
        
        
def init_pos_params():
    '''初始化相关参数'''
    util = Utils()
    try:
        myDes = MyDes()
        params_handler = open('yeepayparams')
        lines = params_handler.readlines()
        for line in lines:
            if line.startswith('终端主密钥密文'):
                master_key = line.split('=')[1]
                #decrypt master key
                if TEST_ENV != 'true':
                    YeepayClient._MASTER_KEY = myDes.des(master_key.strip(), YeepayClient._TRANSPORT_KEY, 1)
            elif line.startswith('POS初始流水号'):
                pos_serial_number = line.split('=')[1]
                #更新pos序列号
                if not util.read_config('pos_serial_number'):
                    util.update_config('pos_serial_number', pos_serial_number.strip())
            elif line.startswith('POS初始批次号'):
                pos_batch_number = line.split('=')[1]
                if not util.read_config('pos_batch_number'):
                    util.update_config('pos_batch_number', pos_batch_number.strip())
            elif line.startswith('商户号'):
                customer_number = line.split('=')[1]
                YeepayClient._YEEPAY_CUSTOMER_NUMBER = customer_number.strip()
            elif line.startswith('终端号'):
                terminal_number = line.split('=')[1]
                YeepayClient._YEEPAY_TERMINAL_NUMBER = terminal_number.strip()
            elif line.startswith('最大冲正次数'):
                rollback_times = line.split('=')[1]
                YeepayClient._YEEPAY_ROLLBACK_TIMES = rollback_times.strip()
            elif line.startswith('TPDU'):
                tpdu = line.split('=')[1]
                YeepayClient._TPDU = tpdu.strip()
            elif line.startswith('软件版本号'):
                software_version = line.split('=')[1]
                YeepayClient._YEEPAY_SOFTWARE_VERSION = software_version.strip()
            elif line.startswith('参数版本号'):
                params_version = line.split('=')[1]
                YeepayClient._YEEPAY_PARAMS_VERSION = params_version.strip()
    except Exception as e:
        print 'exception is:',e
    finally:
        params_handler.close()
    
def poweron_initialize():
    '''开机自检，初始化操作'''
    from printer import init_printer
    param_file = os.path.join(CURRENTDIR,'yeepayparams')
    ##参数文件不存在，请求参数下发
    yeepayClient = YeepayClient()
    
    util = Utils()
    #------------- 变更终端号获取方式 start ---------------#
    mac = get_mac()
    if mac:
        result = urllib.urlopen('%s/%s/%s'%(URL,'get_sn',mac))
        sn_res = result.read().strip() #获取未使用的终端号，序列号，格式为：tn#sn
        if sn_res:
            tn,sn = sn_res.split('#')
            util.update_config('terminal_number', tn)
            util.update_config('terminal_serial_number', sn)
            YeepayClient._YEEPAY_TERMINAL_NUMBER = tn
            YeepayClient._YEEPAY_SERIAL_NUMBER = sn
            yeepayClient.download_posp_params() #下载主密钥等参数
        
#    tn_result = util.read_config('terminal_number')
#    #如果配置文件中不存在终端号，说明从未获取过终端号和序列号，从服务器获取并写入到配置文件
#    if not tn_result: 
#        result = urllib.urlopen('%s/%s'%(URL,'get_sn'))
#        sn_res = result.read().strip() #获取未使用的终端号，序列号，格式为：tn#sn
#        if sn_res:
#            tn,sn = sn_res.split('#')
#            util.update_config('terminal_number', tn)
#            util.update_config('terminal_serial_number', sn)
#        else: #获取到的终端号序列号为空，只有当库存中不存在了终端号才会出现这种情况
#            print 'get terminal number and serial number error.'
    #------------- 变更终端号获取方式 end ---------------#
    
    YeepayClient._YEEPAY_TERMINAL_NUMBER = util.read_config('terminal_number')
    YeepayClient._YEEPAY_SERIAL_NUMBER = util.read_config('terminal_serial_number')
    if not os.path.exists(param_file):
        yeepayClient.download_posp_params() #下载主密钥等参数
    #初始化打印机，设置黑标等
    init_printer(conf['printer_port'])
    init_pos_params()
    result = yeepayClient.signin()
    
conf = load_config()
URL = conf['url'][:-1] if conf['url'].endswith('/') else conf['url']
TEST_ENV = conf['test_env'].strip()
YeepayClient._SERVER_IP = conf['yeepay_server_ip']
YeepayClient._SERVER_PORT = conf['yeepay_server_port']
YeepayClient._TRANSPORT_KEY = conf['transport_key']
#com端口
KeyboardClient._COM_PORT = int(conf['posboard_port'].replace('com',''))

if __name__ == "__main__":
#检查网络
    while True:
        try:
            result = urllib.urlopen('%s/%s'%("http://117.41.181.89:8000",'version'))
            version = float(result.read().strip())
#            sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#            sock.connect(('117.41.181.89',8000))
            break
        except Exception as e:
            print "network error[10065] for http access:",e
            time.sleep(1)
            continue
    app = QtGui.QApplication(sys.argv)
    if conf['hidemouse'] == 'true':
        app.setOverrideCursor(QtCore.Qt.BlankCursor) #隐藏鼠标
    main = main_app()
    main.show()
    
    #开机自检
    if sys.platform == 'win32':
        try:
            poweron_initialize()
        except Exception as e:
            print 'error is:%s'%e
    
    sys.exit(app.exec_())
